#include "util.h"

void usage() {
    fprintf(stderr, "usage: cmus-rg [-w WIDTH] [-h HEIGHT] [-f FONT] [-s FONT_SIZE]\n");
    exit(EXIT_FAILURE);
}

void die(int exit_code, char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
 
    /* the trained eye will notice that I copy pasted this from cppreference */
    int i = 0;
    while (fmt[i] != '\0') {
        if (fmt[i] == '%') {
            ++i;
            continue;
        } else if (i > 0 && fmt[i - 1] == '%' && fmt[i] == 'd') {
            fprintf(stderr, "%d", va_arg(args, int));
        } else if (i > 0 && fmt[i - 1] == '%' && fmt[i] == 'i') {
            fprintf(stderr, "%i", va_arg(args, int));
        } else if (i > 0 && fmt[i - 1] == '%' && fmt[i] == 'c') {
            /* char gets promoted to int */
            fprintf(stderr, "%c", va_arg(args, int));
        } else if (i > 0 && fmt[i - 1] == '%' && fmt[i] == 'f') {
            fprintf(stderr, "%f", va_arg(args, double));
        } else if (i > 0 && fmt[i - 1] == '%' && fmt[i] == 's') {
            fprintf(stderr, "%s", va_arg(args, char*));
        } else {
            fprintf(stderr, "%c", fmt[i]);
        }
        ++i;
    }
 
    va_end(args);
    exit(EXIT_FAILURE);
}

void parse_user_options(int argc, char **argv, int *window_w, int *window_h, char **font, int *font_size) {
    int opt_window_w = DEFAULT_WINDOW_W;
    int opt_window_h = DEFAULT_WINDOW_H;
    int opt_font_size = DEFAULT_FONT_SIZE;
    char *opt_font = NULL;
    bool opt_verbose = DEBUG;

    char c;
    /* parse arguments */
    while ((c = getopt(argc, argv, "w:h:s:f:v")) != -1) {
        switch (c) {
            case 'w':
                opt_window_w = strtol(optarg, NULL, 0);
                if (opt_window_w == 0 && (errno == EINVAL || errno == ERANGE))
                    die(EXIT_FAILURE, "%s: error converting WIDTH: %s\n", argv[0], strerror(errno));
                break;
            case 'h':
                opt_window_h = strtol(optarg, NULL, 0);
                if (opt_window_h == 0 && (errno == EINVAL || errno == ERANGE))
                    die(EXIT_FAILURE, "%s: error converting HEIGHT: %s\n", argv[0], strerror(errno));
                break;
            case 's':
                opt_font_size = strtol(optarg, NULL, 0);
                if (opt_font_size == 0 && (errno == EINVAL || errno == ERANGE))
                    die(EXIT_FAILURE, "%s: error converting FONT_SIZE: %s\n", argv[0], strerror(errno));
                break;
            case 'f':
                opt_font = optarg;
                break;
            case 'v':
                opt_verbose = true;
                break;
            /* unsupported arugments */
            case '?':
                usage();
            /* should never happen */
            default:
                assert(0);
        }
    }

    *window_w = opt_window_w;
    *window_h = opt_window_h;
    *font = opt_font;
    *font_size = opt_font_size;
}

uint64_t hash(unsigned char *str) {
    uint64_t hash = 5381;
    int c;

    while (c = *str++)
        /* hash * 33 + c */
        hash = ((hash << 5) + hash) + c; 

    return hash;
}

void scale_sdl_rect_center(SDL_Rect *rect, int w, int h, int max_w, int max_h) {
    int offset_w = 0, offset_h = 0;

    /* if we need to scale down */
    if (w > max_w || h > max_h) {
        float ratio_w = (float) w / (float) max_w;
        float ratio_h = (float) h / (float) max_h;
        /* we decide to follow the less extreme conversion, that */
        /* is to say the lesser ratio */
        if (ratio_w > ratio_h) {
            w /= ratio_h;
            h /= ratio_h;
        } else {
            w /= ratio_w;
            h /= ratio_w;
        }
    /* if we need to scale up */
    } else if (w < max_w && h < max_h) {
        float ratio_w = (float) max_w / (float) w;
        float ratio_h = (float) max_h / (float) h;
        if (ratio_h > ratio_w) {
            w *= ratio_h;
            h *= ratio_h;
        } else {
            w *= ratio_w;
            h *= ratio_w;
        }
    }

    /* if a side is still larget than the max, we need to offset the */
    /* image to effectively center it */
    if (w > max_w)
        offset_w = -((w - max_h) / 2);
    if (h > max_h)
        offset_h = -((h - max_h) / 2);

    /* scale rect */
    rect->x = offset_w;
    rect->y = offset_h;
    rect->w = w;
    rect->h = h;
}

cr_track* create_cr_track(uint64_t duration, char *file, char *album_tag, char *albumartist_tag, char *artist_tag, char *comment_tag, char *date_tag, char *genre_tag, char *title_tag, uint64_t discnumber_tag, uint64_t cr_tracknumber_tag, char *stream_tag) {
    cr_track *cr_track = malloc(sizeof(*cr_track));

    cr_track->duration = duration;
    cr_track->file = file;
    cr_track->album_tag = album_tag;
    cr_track->albumartist_tag = albumartist_tag;
    cr_track->artist_tag = artist_tag;
    cr_track->comment_tag = comment_tag;
    cr_track->date_tag = date_tag;
    cr_track->genre_tag = genre_tag;
    cr_track->title_tag = title_tag;
    cr_track->discnumber_tag = discnumber_tag;
    cr_track->cr_tracknumber_tag = cr_tracknumber_tag;
    cr_track->stream = stream_tag;

    return cr_track;
}

void destroy_cr_track(cr_track *cr_track) {
    free(cr_track->file);
    free(cr_track->album_tag);
    free(cr_track->albumartist_tag);
    free(cr_track->artist_tag);
    free(cr_track->comment_tag);
    free(cr_track->date_tag);
    free(cr_track->genre_tag);
    free(cr_track->title_tag);
    free(cr_track->stream);
    free(cr_track);
}

void print_cr_track(cr_track *cr_track) {
    printf(
        "cr_track { duration %u, file %s, album_tag %s, albumartist_tag %s, artist_tag %s, comment_tag %s, date_tag %s, genre_tag %s, title_tag %s, discnumber_tag %u, cr_tracknumber_tag %u, stream %s }\n",
        cr_track->duration,
        cr_track->file,
        cr_track->album_tag,
        cr_track->albumartist_tag,
        cr_track->artist_tag,
        cr_track->comment_tag,
        cr_track->date_tag,
        cr_track->genre_tag,
        cr_track->title_tag,
        cr_track->discnumber_tag,
        cr_track->cr_tracknumber_tag,
        cr_track->stream
    );
}

cr_player* create_cr_player(char *aaa_mode, char *status, uint64_t vol_left, uint64_t vol_right, uint64_t position, bool repeat, bool repeat_current, bool shuffle) {
    cr_player *cr_player = malloc(sizeof(*cr_player));

    cr_player->aaa_mode = aaa_mode;
    cr_player->status = status;
    cr_player->vol_left = vol_left;
    cr_player->vol_right = vol_right;
    cr_player->position = position;
    cr_player->repeat = repeat;
    cr_player->repeat_current = repeat_current;
    cr_player->shuffle = shuffle;

    return cr_player;    
}

void destroy_cr_player(cr_player *cr_player) {
    free(cr_player->aaa_mode);
    free(cr_player->status);
    free(cr_player);
}

void print_cr_player(cr_player *cr_player) {
    printf(
        "cr_player { aaa_mode %s, status %s, vol_left %u, vol_right %u, position %u, repeat %i, repeat_current %i, shuffle %i }\n",
        cr_player->aaa_mode,
        cr_player->status,
        cr_player->vol_left,
        cr_player->vol_right,
        cr_player->position,
        cr_player->repeat,
        cr_player->repeat_current,
        cr_player->shuffle
    );
}

/* extract rgba part from uint32_t */
int partr(uint32_t color) {
    return (color & 0xFF000000) >> 24;
}

int partg(uint32_t color) {
    return (color & 0x00FF0000) >> 16;
}

int partb(uint32_t color) {
    return (color & 0x0000FF00) >> 8;
}

int parta(uint32_t color) {
    return (color & 0x000000FF) >> 0;
}

/* calculate position of texture to be placed */
/* negative numbers automatically offset from the opposite side */
int posx(int x, int w, int max_w) {
    if (x < 0)
        return max_w - w + x;
    else
        return x;
}

int posy(int y, int h, int max_h) {
    if (y < 0)
        return max_h - h + y;
    else
        return y;
}
